<template>
  <GlobalWindow
    class="permission-config-dialog"
    v-model:visible="visible"
    :confirm-working="isWorking"
    width="650px"
    title="授权权限"
    @confirm="confirm"
  >
    <div
      v-if="role != null"
      class="tip-wrap"
    >
      <p>为角色 <em>{{role.name}}</em> 配置权限</p>
      <el-alert
        title="操作提示：在左侧选择需要配置的权限类型，在右侧勾选对应的权限，全部配置完成后点击底部「确认」按钮即可。配置完成后，对应角色需重新登录账号来使用最新权限！"
        type="info"
        :closable="false"
      />
    </div>
    <el-tabs
      v-model="permissionTabs.active"
      tab-position="left"
      @tab-click="handleTabClick"
    >
      <el-tab-pane name="menu" label="菜单和功能">
        <ul class="settings">
          <li>
            <el-checkbox
              :model-value="menuSettings.collapse"
              @click="handleCollapseChange"
            >全部展开/折叠</el-checkbox>
          </li>
          <li>
            <el-checkbox
              :model-value="menuSettings.selectedAll"
              @click="handleSelectAll"
            >全选</el-checkbox>
          </li>
          <li>
            <el-checkbox
              :model-value="menuSettings.checkStrictly"
              @change="this.menuSettings.checkStrictly = !this.menuSettings.checkStrictly"
            >父子联动</el-checkbox>
          </li>
        </ul>
        <el-tree
          ref="menuTree"
          :data="permissionTabs.menu.permissions"
          show-checkbox
          :check-strictly="!menuSettings.checkStrictly"
          node-key="id"
          default-expand-all
          :default-checked-keys="permissionTabs.menu.selectedIds"
          :expand-on-click-node="false"
          :check-on-click-node="true"
          @check-change="handleNodeClick"
        />
      </el-tab-pane>
      <el-tab-pane name="systemConfig" label="系统配置项">
        <EmptyTip
          v-if="permissionTabs.systemConfig.permissions.length === 0"
          description="没有系统配置项可配置"
        />
        <el-alert
          v-else-if="!permissionTabs.systemConfig.visible"
          title="当前角色没有「系统配置」菜单访问权限，无需配置系统配置项权限！"
          type="warning"
          :closable="false"
        />
        <template v-else>
          <el-alert
            title="提醒：系统配置项权限用于限定角色对系统配置项的访问权限，但不会影响系统配置是否能加载到客户端。当配置项没有设定权限标识符时无法选中，默认可访问。"
            type="warning"
            :closable="false"
            style="margin-bottom: 10px;"
          />
          <el-tree
            ref="systemConfigTree"
            :data="permissionTabs.systemConfig.permissions"
            show-checkbox
            :check-strictly="true"
            node-key="id"
            default-expand-all
            :default-checked-keys="permissionTabs.systemConfig.selectedIds"
            :expand-on-click-node="false"
            :check-on-click-node="true"
          />
        </template>
      </el-tab-pane>
    </el-tabs>
  </GlobalWindow>
</template>

<script>
import { fetchPermissions, configPermissions } from '@/api/system/permission'
import { mapState } from 'pinia'
import { useDefaultStore } from '@/core/store'
export default {
  name: 'PermissionConfigWindow',
  data () {
    return {
      visible: false,
      isWorking: false,
      // 角色对象
      role: null,
      // 权限页签
      permissionTabs: {
        active: 'menu',
        // 菜单和功能
        menu: {
          selectedIds: [],
          permissions: []
        },
        // 系统配置
        systemConfig: {
          visible: false,
          selectedIds: [],
          permissions: []
        }
      },
      // 菜单和功能设置
      menuSettings: {
        collapse: true,
        selectedAll: false,
        checkStrictly: false
      },
      // 默认的菜单和功能配置
      defaultMenuSettings: null
    }
  },
  computed: {
    ...mapState(useDefaultStore, ['userInfo'])
  },
  methods: {
    /**
     * 打开窗口
     *
     * @param role 角色对象
     */
    open (role) {
      // 重置菜单和功能配置
      this.menuSettings = { ...this.defaultMenuSettings }
      // 获取所有权限配置节点数据
      fetchPermissions()
        .then(data => {
          this.role = role
          this.permissionTabs.active = 'menu'
          // 处理菜单和功能权限，仅保留需要配置的节点
          this.permissionTabs.menu.permissions = this.__handlePermissions(data.menuNodes)
          // 处理系统配置权限，仅保留需要配置的节点
          this.permissionTabs.systemConfig.permissions = this.__handlePermissions(data.configNodes)
          // 获取默认选中的菜单和功能权限
          const permissions = []
          this.__treeToArray(this.permissionTabs.menu.permissions, permissions)
          // 获取当前角色已配置的菜单和功能权限ID作为默认选中
          this.permissionTabs.menu.selectedIds = permissions
            .filter(p => role.permissions.findIndex(rp => rp.id === p.id) !== -1)
            .map(p => p.id)
          // 获取当前角色已配置的配置权限ID作为默认选中
          this.permissionTabs.systemConfig.selectedIds = this.permissionTabs.systemConfig.permissions
            .filter(p => role.permissions.findIndex(rp => rp.id === p.id) !== -1)
            .map(p => p.id)
          this.visible = true
          // 处理全选状态
          this.$nextTick(() => {
            this.handleSelectedAllStatus()
          })
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
    },
    /**
     * 确认选择权限
     */
    confirm () {
      // 获取菜单权限
      const menuPermissionIds = this.$refs.menuTree.getCheckedNodes(false, true)
        // 筛选出有效的权限
        .filter(item => item.type === 'menu')
        .map(item => item.id)
      // 获取功能权限
      const funcPermissionIds = this.$refs.menuTree.getCheckedNodes(false, true)
        // 筛选出有效的权限
        .filter(item => item.type === 'func')
        .map(item => item.id)
      // 获取系统配置项权限
      let systemConfigPermissionIds = null
      if (this.$refs.systemConfigTree) {
        systemConfigPermissionIds = this.$refs.systemConfigTree.getCheckedNodes(false, true)
          // 筛选出有效的权限
          .filter(item => item.type === 'systemConfig' && item.id > 0)
          .map(item => item.id)
      }
      // 执行授权
      this.isWorking = true
      configPermissions({
        roleId: this.role.id,
        menuPermissionIds,
        funcPermissionIds,
        systemConfigPermissionIds
      })
        .then(() => {
          this.visible = false
          this.$emit('success')
          setTimeout(() => {
            this.$dialog.attentionConfirm('权限配置成功，用户需重新登录后生效！').then(() => {})
          }, 300)
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
        .finally(() => {
          this.isWorking = false
        })
    },
    /**
     * 切换标签
     *
     * @param paneName 已选tab的名称
     */
    handleTabClick ({ paneName }) {
      // 超级管理员
      if (this.$isSuperAdmin()) {
        this.permissionTabs.systemConfig.visible = true
        return
      }
      // 非超级管理员，检查是否拥有系统配置菜单权限
      if (paneName === 'systemConfig') {
        this.permissionTabs.systemConfig.visible = false
        const menus = this.$refs.menuTree.getCheckedNodes(false, true)
          // 筛选出有效的权限
          .filter(item => item.type === 'menu' && item.id > 0)
        // 判断是否存在系统配置菜单
        if (menus.find(item => item.permission === 'system:config:query') != null) {
          this.permissionTabs.systemConfig.visible = true
        }
      }
    },
    /**
     * 全选
     */
    handleSelectAll () {
      // 取消全选
      if (this.menuSettings.selectedAll) {
        this.$refs.menuTree.setCheckedNodes([])
        this.menuSettings.selectedAll = false
        return
      }
      // 全选
      const permissions = []
      this.__treeToArray(this.permissionTabs.menu.permissions, permissions)
      this.$refs.menuTree.setCheckedNodes(permissions)
      this.menuSettings.selectedAll = true
    },
    /**
     * 处理全选状态
     */
    handleSelectedAllStatus () {
      // 获取实际勾选权限节点
      const nodes = this.$refs.menuTree.getCheckedNodes()
        .filter(item => item.type !== 'dir')
      // 获取所有非目录权限节点
      let permissions = []
      this.__treeToArray(this.permissionTabs.menu.permissions, permissions)
      permissions = permissions.filter(p => p.type !== 'dir')
      // 如果实际勾选权限节点 = 所有非目录权限节点，视为全选状态
      if (nodes.length === permissions.length) {
        // 标记全选状态为false，使发生全选动作
        this.menuSettings.selectedAll = false
        // 此处触发一次全选动作，将目录也勾选上
        this.handleSelectAll()
      }
    },
    /**
     * 节点被点击
     *
     * @param node 节点对象
     * @param state 节点勾选状态
     */
    handleNodeClick (node, state) {
      // 勾选节点，需处理全选状态
      if (state) {
        this.handleSelectedAllStatus()
        return
      }
      // 取消勾选，全选状态置为false
      this.menuSettings.selectedAll = false
    },
    /**
     * 展开/折叠
     */
    handleCollapseChange () {
      // 折叠
      if (this.menuSettings.collapse) {
        Object.values(this.$refs.menuTree.store.nodesMap).forEach((v) => v.collapse())
        this.menuSettings.collapse = false
        return
      }
      // 展开
      Object.values(this.$refs.menuTree.store.nodesMap).forEach((v) => v.expand())
      this.menuSettings.collapse = true
    },
    /**
     * 处理权限树
     * 1. 过滤掉无需权限配置的节点
     * 2. 过滤掉空目录
     * 3. 为目录生成负数ID，避免node-key为null
     *
     * @param tree 权限树
     * @returns {*}
     * @private
     */
    __handlePermissions (tree) {
      return tree
        .map(item => {
          // 过滤掉无需权限配置的节点
          if (item.type !== 'dir' && item.id == null) {
            return null
          }
          // 过滤掉空目录
          if (item.type === 'dir' && (item.children == null || item.children.length === 0)) {
            return null;
          }
          if (item.children != null && item.children.length > 0) {
            item.children = this.__handlePermissions(item.children)
            // 再次过滤掉空目录
            if (item.type === 'dir' && (item.children == null || item.children.length === 0)) {
              return null;
            }
          }
          // 为目录生成负数ID，避免node-key为null
          const id = item.id == null ? (0 - Math.random()) : item.id
          return {
            ...item,
            id
          }
        })
        .filter(item => item != null)
    },
    /**
     * 将权限树转为权限数组
     *
     * @param tree 权限树
     * @param array 节点列表，用于存放权限节点
     * @private
     */
    __treeToArray (tree, array) {
      if (tree == null) {
        return
      }
      for (const permission of tree) {
        array.push(permission)
        if (permission.children != null) {
          this.__treeToArray(permission.children, array)
        }
      }
    }
  },
  created () {
    this.defaultMenuSettings = { ...this.menuSettings }
  }
}
</script>

<style lang="scss">
.permission-config-dialog {
  .window__body {
    overflow-y: hidden !important;
    display: flex;
    flex-direction: column;
    .tip-wrap {
      flex-shrink: 0;
      & > p {
        margin: 0;
        em {
          font-style: normal;
          color: var(--color-primary);
          font-weight: bold;
        }
      }
      .el-alert {
        margin: 10px 0;
      }
    }
    .el-tabs {
      flex-grow: 1;
      .el-tabs__content {
        height: 100%;
        overflow-y: auto;
        // 菜单和功能
        #pane-menu {
          height: 100%;
          display: flex;
          flex-direction: column;
          overflow: hidden;
          // 设置
          .settings {
            flex-shrink: 0;
            padding: 10px;
            border-bottom: 1px solid var(--border-color);
            margin-bottom: 10px;
            display: flex;
            li {
              margin-right: 20px;
            }
          }
          // 菜单和功能数
          .el-tree {
            flex-grow: 1;
            overflow-y: auto;
          }
        }
      }
    }
  }
}
</style>
